// 参考 https://github.com/stemkoski/stemkoski.github.com/blob/master/Three.js/Shader-Glow.html
import { 
  AdditiveBlending,
  AmbientLight,
  BoxGeometry,
  Color,
  FrontSide,
  Mesh,
  MeshLambertMaterial,
  MeshStandardMaterial,
  PerspectiveCamera,
  PlaneGeometry,
  Scene, 
  ShaderMaterial, 
  SphereGeometry, 
  SpotLight, 
  WebGLRenderer
} from "three";
import { TrackballControls } from 'three/examples/jsm/controls/TrackballControls';
import TWEEN from '@tweenjs/tween.js';

const scene = new Scene();
const camera = new PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);
camera.position.set(-30, 40, 30);
camera.lookAt(scene.position);
const renderer = new WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);

scene.add(new AmbientLight(0xaaaaaa));

const spotLight = new SpotLight(0xffffff, 0.2);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
scene.add(spotLight);

const planeGeometry = new PlaneGeometry(60, 40);
const planeMaterial = new MeshLambertMaterial({color: 0x0b2540});
const plane = new Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = Math.PI/-2;
scene.add(plane);

const boxGeometry = new BoxGeometry(4, 4, 4);
const boxMaterial = new MeshStandardMaterial({
  color: 0xff9900
});
const box = new Mesh(boxGeometry, boxMaterial);
box.position.set(0, 2, 0);
scene.add(box);
// 片元着色器
const fragmentShader = `
#include <common>

uniform vec3 glowColor;
varying float intensity;

void main() {
  vec3 glow = glowColor * intensity;
  gl_FragColor = vec4(glow, 1.0);
}
`;
const vertexShader = `
uniform vec3 viewVector;
uniform float c;
uniform float p;
varying float intensity;

void main() {
  vec3 vNormal = normalize(normalMatrix * normal);
  vec3 vNormel = normalize(normalMatrix * viewVector);

  intensity = pow(c - dot(vNormal, vNormel), p);
  gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`
const sphereGeometry = new SphereGeometry(10, 50, 50, 0, Math.PI*2, 0, Math.PI/2);
const sphereMaterial = new MeshStandardMaterial({
  color: 0xffff00,
  transparent: true,
  opacity: 0.2,
  // 定义混合模式
  blending: AdditiveBlending,
  // depthTest: false,
  // alphaTest: 1,
  side: FrontSide,
});
const sphereShaderMaterial = new ShaderMaterial({
  vertexShader,
  fragmentShader,
  // transparent: true,
  // opacity: 0.2,
  // 定义混合模式
  blending: AdditiveBlending,
  // alphaTest: 1,
  // side: FrontSide,
  // depthTest: true,
  depthWrite: false,
  uniforms: {
    glowColor: { value: new Color(0xff0000) },
    c: { value: 1.0 },
    p: { value: 1.4 },
    viewVector: { value: camera.position }
  },
});
const sphere1 = new Mesh(sphereGeometry, sphereMaterial);
sphere1.position.x = 10;

const sphere2 = new Mesh(sphereGeometry, sphereShaderMaterial);
const sphere3 = new Mesh(sphereGeometry, sphereShaderMaterial);
sphere3.position.x = -10;

scene.add(sphere1);
scene.add(sphere2);
scene.add(sphere3);

const trackballControls = new TrackballControls(camera, renderer.domElement);

function animate() {
  new TWEEN.Tween({value: 0}).to({value: 1}, 2000)
    // https://sole.github.io/tween.js/examples/03_graphs.html
    .easing(TWEEN.Easing.Quadratic.Out)
    .onUpdate(({value}) => {
      sphere1.scale.set(value, value, value);
      sphere2.scale.set(value, value, value);
      sphere3.scale.set(value, value, value);
    })
    .start()
    .repeat(Infinity)
}

function render() {
  TWEEN.update();
  trackballControls.update();
  requestAnimationFrame(render);
  renderer.render(scene, camera);
}

render();
animate();